package com.iplant.aps.service.po.sch;

import com.iplant.aps.service.po.aps.APSCapacityUsed;
import com.iplant.base.utils.general.CalendarTool;
import com.iplant.basic.service.basicenum.bfc.FMCShiftType;
import com.iplant.basic.service.po.fmc.FMCShift;
import com.iplant.basic.service.po.fmc.FMCUnavailable;
import com.iplant.basic.serviceImpl.dao.fmc.FMCShiftDAO;
import com.iplant.pdm.service.po.fpc.FPCCapacity;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 每天剩余产能
 */
public class APSMarginDayCapacity {

    /**
     * 日期
     */
    public Calendar CurrentDate = Calendar.getInstance();

    public int EntityID = 0;


    /**
     * 获取当天时间点之后的所有班次产能
     *
     * @param wProductID
     * @param wUsedTime
     * @param wShiftEnable
     * @return
     */
    public float getCapacity(int wProductID, Calendar wUsedTime, boolean wShiftEnable) {

        //根据时间获取班次
        FMCShiftType wFMCShiftType = FMCShiftDAO.getInstance().FMC_GetShiftType(ShiftMap.values().stream().collect(Collectors.toList()), wUsedTime);
        if (wFMCShiftType == FMCShiftType.Default)
            return 0;

        if (!SetCapacity.containsKey(wFMCShiftType))
            return 0;

        if (!SetCapacity.get(wFMCShiftType).containsKey(wProductID))
            return 0;
        if (!MarginCapacity.containsKey(wFMCShiftType))
            return 0;

        if (!MarginCapacity.get(wFMCShiftType).containsKey(wProductID))
            return 0;

        float wCapacityTotal = 0f;
        if (wShiftEnable) {
            boolean wIsUse = false;
            for (FMCShift wFMCShift : ShiftMap.values()) {
                if (wFMCShift == null || wFMCShift.Active != 1)
                    continue;
                if (wFMCShift.ShiftType == wFMCShiftType.getValue())
                    wIsUse = true;
                if (wIsUse) {
                    wFMCShiftType = FMCShiftType.getEnumType(wFMCShift.ShiftType);
                    if (wFMCShiftType == FMCShiftType.Default)
                        continue;

                    wCapacityTotal += MarginCapacity.get(wFMCShiftType).get(wProductID).floatValue();
                }

            }
        } else {
            for (FMCShift wFMCShift : ShiftMap.values()) {
                if (wFMCShift == null || wFMCShift.Active != 1)
                    continue;
                wFMCShiftType = FMCShiftType.getEnumType(wFMCShift.ShiftType);
                if (wFMCShiftType == FMCShiftType.Default)
                    continue;

                wCapacityTotal += MarginCapacity.get(wFMCShiftType).get(wProductID).floatValue();
            }
        }

        return wCapacityTotal;
    }


    public float UseCapacity(int wProductID, int wPartPointID, float wCapacity, FMCShiftType wFMCShiftType, boolean wIsDefult) {

        if (wFMCShiftType == FMCShiftType.Default)
            return 0;
        if (!SetCapacity.containsKey(wFMCShiftType) || !SetCapacity.get(wFMCShiftType).containsKey(wProductID))
            return 0;


        if (!MarginCapacity.containsKey(wFMCShiftType) || !MarginCapacity.get(wFMCShiftType).containsKey(wProductID) || MarginCapacity.get(wFMCShiftType).get(wProductID) <= 0)
            return 0;


        if (MarginCapacity.get(wFMCShiftType).get(wProductID) < wCapacity) {
            wCapacity = MarginCapacity.get(wFMCShiftType).get(wProductID).floatValue();
        }


        //
        Map<Integer, FPCCapacity> wFPCCapacityMap = SetCapacity.get(wFMCShiftType);
        FPCCapacity wFPCCapacity = wFPCCapacityMap.get(wProductID);
        double wMarginRate = (MarginCapacity.get(wFMCShiftType).get(wProductID) - wCapacity) / wFPCCapacity.CapacityNum;

        if (wFPCCapacityMap.get(wProductID).MixEnable != 1) {
            wFPCCapacityMap.get(wProductID).MaxMixNumber = 1;
        }


        if (UsedProductList.containsKey(wFMCShiftType) && UsedProductList.get(wFMCShiftType) != null
                && UsedProductList.get(wFMCShiftType).size() >= wFPCCapacityMap.get(wProductID).MaxMixNumber && !UsedProductList.get(wFMCShiftType).contains(wProductID))
            return 0;

        if (!UsedProductList.containsKey(wFMCShiftType))
            UsedProductList.put(wFMCShiftType, new ArrayList<>());

        if (!UsedProductList.get(wFMCShiftType).contains(wProductID))
            UsedProductList.get(wFMCShiftType).add(wProductID);

        for (Integer wProductIDT : wFPCCapacityMap.keySet()) {
            if (wProductIDT == wProductID) {
                MarginCapacity.get(wFMCShiftType).put(wProductIDT, MarginCapacity.get(wFMCShiftType).get(wProductIDT) - wCapacity);
                continue;
            }

            if (wFPCCapacityMap.get(wProductIDT).MixEnable != 1) {
                //不允许生产 将自身排除
                MarginCapacity.get(wFMCShiftType).put(wProductIDT, 0.0);
                continue;
            }
            if (UsedProductList.get(wFMCShiftType).size() >= wFPCCapacityMap.get(wProductIDT).MaxMixNumber && !UsedProductList.get(wFMCShiftType).contains(wProductIDT)) {
                //混合生产数量达到 但是未生产自身 将自身排除
                MarginCapacity.get(wFMCShiftType).put(wProductIDT, 0.0);
                continue;
            }
            if (wFPCCapacityMap.get(wProductIDT).MutexEnable == 1) {
                //产能互斥才需要操作，不互斥无需操作
                if (wFPCCapacity.MutexEnable == 1) {
                    MarginCapacity.get(wFMCShiftType).put(wProductIDT, (wMarginRate * wFPCCapacityMap.get(wProductIDT).CapacityNum));
                }

            }

        }

        if (!wIsDefult) {
            if (!ProductUsedTime.containsKey(wFMCShiftType))
                ProductUsedTime.put(wFMCShiftType, new HashMap<>());

            if (!ProductUsedTime.get(wFMCShiftType).containsKey(wProductID))
                ProductUsedTime.get(wFMCShiftType).put(wProductID, new HashMap<>());


            if (!ProductUsedTime.get(wFMCShiftType).get(wProductID).containsKey(wPartPointID)) {
                if (ShiftMap.containsKey(wFMCShiftType.getValue())) {

                    ProductUsedTime.get(wFMCShiftType).get(wProductID).put(wPartPointID, new APSCapacityUsed(EntityID, wProductID,wPartPointID,
                            CurrentDate, CalendarTool.CloneDateAndTime(CurrentDate, ShiftMap.get(wFMCShiftType.getValue()).StartTime), wFMCShiftType.getValue(), wFPCCapacity.CapacityNum, wCapacity));
                } else {
                    ProductUsedTime.get(wFMCShiftType).get(wProductID).put(wPartPointID, new APSCapacityUsed(EntityID, wProductID,wPartPointID,
                            CurrentDate, CurrentDate, wFMCShiftType.getValue(), wFPCCapacity.CapacityNum, wCapacity));
                }

            } else {
                ProductUsedTime.get(wFMCShiftType).get(wProductID).get(wPartPointID).UsedCapacity += wCapacity;
            }
        }
        return wCapacity;
    }

    /**
     * 使用时间之后所有班次产能
     *
     * @param wProductID
     * @param wCapacity
     * @param wUsedTime
     * @param wShiftEnable
     * @return
     */
    public float UseCapacity(int wProductID,int wPartPointID, float wCapacity, Calendar wUsedTime, boolean wShiftEnable) {
        FMCShiftType wFMCShiftType = FMCShiftDAO.getInstance().FMC_GetShiftType(ShiftMap.values().stream().collect(Collectors.toList()), wUsedTime);
        if (wFMCShiftType == FMCShiftType.Default)
            return 0;

        float wCapacityTotal = 0;

        if (wShiftEnable) {
            boolean wIsUse = false;
            for (FMCShift wFMCShift : ShiftMap.values()) {
                if (wFMCShift == null || wFMCShift.Active != 1)
                    continue;
                if (wFMCShift.ShiftType == wFMCShiftType.getValue())
                    wIsUse = true;
                if (wIsUse)
                    wCapacityTotal += UseCapacity(wProductID, wPartPointID,wCapacity - wCapacityTotal, FMCShiftType.getEnumType(wFMCShift.ShiftType), false);
                if (wCapacityTotal >= wCapacity)
                    break;
            }
        } else {
            for (FMCShift wFMCShift : ShiftMap.values()) {
                if (wFMCShift == null || wFMCShift.Active != 1)
                    continue;
                wCapacityTotal += UseCapacity(wProductID, wPartPointID,wCapacity - wCapacityTotal, FMCShiftType.getEnumType(wFMCShift.ShiftType), false);
                if (wCapacityTotal >= wCapacity)
                    break;
            }
        }
        return wCapacityTotal;
    }


    /**
     * 班次列表
     */
    public Map<Integer, FMCShift> ShiftMap = new HashMap<>();


    public Map<FMCShiftType, List<Integer>> UsedProductList = new HashMap<>();

    /**
     * 每个班次 每个产品的设置产能
     */
    public Map<FMCShiftType, Map<Integer, FPCCapacity>> SetCapacity = new HashMap<>();

    /**
     * 每个班次 每个产品的剩余产能
     */
    public Map<FMCShiftType, Map<Integer, Double>> MarginCapacity = new HashMap<>();

    /**
     * 需要查询已开工的记录进行初始化
     * Map<Integer,List<APSOrderUsed> key  产品型号
     */
    public Map<FMCShiftType, Map<Integer, Map<Integer, APSCapacityUsed>>> ProductUsedTime = new HashMap<>();


    public List<APSCapacityUsed> getProductUsedTime(int wProductID) {
        List<APSCapacityUsed> wResult = new ArrayList<>();

        for (FMCShiftType wFMCShiftType : ProductUsedTime.keySet()) {
            if (!ProductUsedTime.get(wFMCShiftType).containsKey(wProductID))
                continue;
            for (Integer integer : ProductUsedTime.get(wFMCShiftType).get(wProductID).keySet()) {
                wResult.add(ProductUsedTime.get(wFMCShiftType).get(wProductID).get(integer));
            }
        }
        return wResult;
    }

    public List<APSCapacityUsed> getProductUsedTime(int wProductID,int wPartPointID) {
        List<APSCapacityUsed> wResult = new ArrayList<>();

        for (FMCShiftType wFMCShiftType : ProductUsedTime.keySet()) {
            if (!ProductUsedTime.get(wFMCShiftType).containsKey(wProductID))
                continue;
            if (!ProductUsedTime.get(wFMCShiftType).get(wProductID).containsKey(wPartPointID))
                continue;

            wResult.add(ProductUsedTime.get(wFMCShiftType).get(wProductID).get(wPartPointID));
        }
        return wResult;
    }


    public void ClearResult() {
        ProductUsedTime = new HashMap<>();
    }

    public APSMarginDayCapacity() {

    }

    public APSMarginDayCapacity(int wEntityID, Calendar wCurrentDate, List<FMCShift> wShiftList, Map<Integer,
            List<FPCCapacity>> wFPCCapacitySetMap, List<APSCapacityUsed> wUseCapacity, List<FMCUnavailable> wEntityUnavailableList) {
        this.EntityID = wEntityID;
        this.CurrentDate = CalendarTool.CloneDate(wCurrentDate);

        List<Integer> wAllowShiftType = new ArrayList<>();
        for (FMCShift wFMCShift : wShiftList) {
            if (wFMCShift == null || wFMCShift.Active != 1)
                continue;
            this.ShiftMap.put(wFMCShift.ShiftType, wFMCShift);
            wAllowShiftType.add(wFMCShift.ShiftType);
        }
        Map<FMCShiftType, Float> wEntityUnavailableMap = this.ShiftRate(wEntityUnavailableList);

        FMCShiftType wFMCShiftType;
        for (Integer wProductId : wFPCCapacitySetMap.keySet()) {
            for (FPCCapacity wFPCCapacity : wFPCCapacitySetMap.get(wProductId)) {

                wFMCShiftType = FMCShiftType.getEnumType(wFPCCapacity.ShiftType);
                if (wFMCShiftType == FMCShiftType.Default)
                    continue;

                if (!this.SetCapacity.containsKey(wFMCShiftType))
                    this.SetCapacity.put(wFMCShiftType, new HashMap<>());

                if (!this.SetCapacity.get(wFMCShiftType).containsKey(wProductId)) {
                    this.SetCapacity.get(wFMCShiftType).put(wProductId, wFPCCapacity);
                }


                if (!this.MarginCapacity.containsKey(wFMCShiftType))
                    this.MarginCapacity.put(wFMCShiftType, new HashMap<>());

                if (!this.MarginCapacity.get(wFMCShiftType).containsKey(wProductId)) {
                    if (wEntityUnavailableMap.containsKey(wFMCShiftType)) {
                        this.MarginCapacity.get(wFMCShiftType).put(wProductId, wFPCCapacity.CapacityNum * wEntityUnavailableMap.get(wFMCShiftType));
                    } else {
                        this.MarginCapacity.get(wFMCShiftType).put(wProductId, wFPCCapacity.CapacityNum);
                    }
                }

                if (!wAllowShiftType.contains(wFPCCapacity.ShiftType))
                    this.MarginCapacity.get(wFMCShiftType).put(wProductId, 0.0);

            }
        }
        if (wUseCapacity != null && wUseCapacity.size() > 0) {
            this.UseCapacity(wUseCapacity);
        }

    }


    private Map<FMCShiftType, Float> ShiftRate(List<FMCUnavailable> wEntityUnavailableList) {

        Map<FMCShiftType, Float> wResult = new HashMap<>();
        FMCShiftType wFMCShiftType;
        float wRate = 1.0f;
        for (FMCUnavailable wFMCUnavailable : wEntityUnavailableList) {
            if (CalendarTool.CompareDate(wFMCUnavailable.StartTime, CalendarTool.AddCalendar(CurrentDate, Calendar.DAY_OF_MONTH, 1)) >= 0
                    || CalendarTool.CompareDate(wFMCUnavailable.EndTime, CurrentDate) <= 0)
                continue;

            for (FMCShift wFMCShift : this.ShiftMap.values()) {
                if (wFMCShift == null || wFMCShift.Active != 1)
                    continue;
                wFMCShiftType = FMCShiftType.getEnumType(wFMCShift.ShiftType);
                if (wFMCShiftType == FMCShiftType.Default)
                    continue;
                if (wFMCShift.EndTime.compareTo(wFMCUnavailable.StartTime) <= 0 || wFMCShift.StartTime.compareTo(wFMCUnavailable.EndTime) >= 0)
                    continue;
                if (!wResult.containsKey(wFMCShiftType))
                    wResult.put(wFMCShiftType, 1.0f);

                if (wFMCShift.StartTime.compareTo(wFMCUnavailable.StartTime) <= 0) {
                    if (wFMCShift.EndTime.compareTo(wFMCUnavailable.EndTime) >= 0) {
                        wRate = 1 - (CalendarTool.SubMilliseconds(wFMCUnavailable.EndTime, wFMCUnavailable.StartTime) / CalendarTool.SubMilliseconds(wFMCShift.EndTime, wFMCShift.StartTime));

                    } else {
                        wRate = 1 - (CalendarTool.SubMilliseconds(wFMCShift.EndTime, wFMCUnavailable.StartTime) / CalendarTool.SubMilliseconds(wFMCShift.EndTime, wFMCShift.StartTime));

                    }
                } else {
                    if (wFMCShift.EndTime.compareTo(wFMCUnavailable.EndTime) >= 0) {
                        wRate = 1 - (CalendarTool.SubMilliseconds(wFMCUnavailable.EndTime, wFMCShift.StartTime) / CalendarTool.SubMilliseconds(wFMCShift.EndTime, wFMCShift.StartTime));

                    } else {
                        wRate = 0f;
                    }
                }
                if (wRate < 0)
                    wRate = 0f;
                if (wRate < wResult.get(wFMCShiftType))
                    wResult.put(wFMCShiftType, wRate);
            }
        }
        return wResult;
    }

    /**
     * @param wUseCapacity Key Date String
     */
    private void UseCapacity(List<APSCapacityUsed> wUseCapacity) {


        for (APSCapacityUsed wAPSCapacityUsed : wUseCapacity) {
            if (wAPSCapacityUsed.EntityID != EntityID)
                continue;
            if (CalendarTool.CompareDate(wAPSCapacityUsed.CurrentDate, CurrentDate) != 0)
                continue;
            FMCShiftType wFMCShiftType = FMCShiftType.getEnumType(wAPSCapacityUsed.ShiftType);
            this.UseCapacity(wAPSCapacityUsed.ProductID,wAPSCapacityUsed.PartPointID, wAPSCapacityUsed.UsedCapacity, wFMCShiftType, true);
        }

    }


    public APSMarginDayCapacity Clone() {
        APSMarginDayCapacity wResult = new APSMarginDayCapacity();
        wResult.CurrentDate = CalendarTool.CloneDateTime(this.CurrentDate);
        wResult.EntityID = this.EntityID;
        wResult.ShiftMap = this.ShiftMap;
        wResult.SetCapacity = this.SetCapacity;

        for (FMCShiftType wKey : UsedProductList.keySet()) {
            wResult.UsedProductList.put(wKey, new ArrayList<>(UsedProductList.get(wKey)));
        }
        for (FMCShiftType wKey : MarginCapacity.keySet()) {
            wResult.MarginCapacity.put(wKey, new HashMap<>(MarginCapacity.get(wKey)));
        }
        for (FMCShiftType wKey : ProductUsedTime.keySet()) {
            wResult.ProductUsedTime.put(wKey, new HashMap<>());
            for (Integer wInteger : ProductUsedTime.get(wKey).keySet()) {
                wResult.ProductUsedTime.get(wKey).put(wInteger, new HashMap<>());

                for (Integer wPartpointID : ProductUsedTime.get(wKey).get(wInteger).keySet()) {
                    wResult.ProductUsedTime.get(wKey).get(wInteger).put(wPartpointID, ProductUsedTime.get(wKey).get(wInteger).get(wPartpointID).Clone());
                }

            }
        }
        return wResult;
    }
}
